package fr.jade.service.nodes.allocator;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.BindingController;
import org.objectweb.fractal.api.control.ContentController;
import org.objectweb.fractal.api.control.IllegalBindingException;
import org.objectweb.fractal.api.control.IllegalContentException;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;

import fr.jade.base.JadeBaseComponent;
import fr.jade.event.Event;
import fr.jade.event.EventFactory;
import fr.jade.event.EventHandler;
import fr.jade.event.EventTag;
import fr.jade.fraclite.api.control.GenericAttributeController;
import fr.jade.fraclite.orb.registry.NamingService;
import fr.jade.reflex.util.Reflex;
import fr.jade.util.FractalNavigator;
import fr.jade.util.JadeException;


/**
 * Basic implementation for node allocation. Implements the allocator interface.
 * @author nono
 * @contact noel.depalma@inrialpes.fr
 */
public class AllocatorImpl extends JadeBaseComponent implements Allocator, BindingController, ContentController{
  
  protected static final String registryItfName = "registry";
  protected NamingService registry = null;

  protected static final String registryconfigItfName = "registryconfig";
  protected GenericAttributeController registryconfig = null;

    protected static final String eventOutItfName = "event-out";
    protected EventHandler eventOut = null;
    
    protected Set<Node>         nodes    = new HashSet<Node>();
    protected Map<String, Node> nodesIdx = new HashMap<String, Node>();

    // ------- BindingController -----------------------------------------------------------------

    public String[] listFc(){
      String[] list = new String[] { registryconfigItfName, registryItfName, eventOutItfName};
        return super.concat(list, super.listFc());
    }

    public Object lookupFc(final String cItf) throws NoSuchInterfaceException {
        if (cItf.equals(registryItfName))
            return registry;
        else if (cItf.equals(registryconfigItfName))
            return registryconfig;
        else if (cItf.equals(eventOutItfName))
          return eventOut;
        else
          return super.lookupFc(cItf);
    }

    public void bindFc(final String cItf, final Object sItf) throws NoSuchInterfaceException, IllegalBindingException, IllegalLifeCycleException {
        if (cItf.equals("registry"))
          registry = (NamingService) sItf;
        else if (cItf.equals("registryconfig"))
          registryconfig = (GenericAttributeController) sItf;
        else if (cItf.equals(eventOutItfName))
          eventOut = (EventHandler) sItf;
        else
          super.bindFc(cItf, sItf);
    }

    public void unbindFc(final String cItf) throws NoSuchInterfaceException, IllegalBindingException, IllegalLifeCycleException {
        if (cItf.equals("registry"))
          registry = null;
        else if (cItf.equals("registryconfig"))
          registryconfig = null;
        else if (cItf.equals(eventOutItfName))
          eventOut = null;
        else
          super.unbindFc(cItf);
    }

    // ----Allocator--------------------------------------------------------------------

    public Component alloc(String pNode) throws AllocatorException {
      log("Alloc node :" + pNode);
      Component node = null;
      
      if (pNode != null) {
        if (!nodesIdx.containsKey(pNode)){
          //throw new AllocatorException(pNode + " don't exists.");
        }else{
          Node n = nodesIdx.get(pNode);
          if(!n.free) throw new AllocatorException(pNode + " already allocated.");
          n.free = false;
          node = n.exec;
        }
      }
      
      for(Node n : nodes){
        if(n.free){
          n.free = false;
          node = n.exec;
          break;
        }
      }

      if(node == null) throw new AllocatorException("no more free node");
      try{
        log(FractalNavigator.getName(node)+" allocated");
      }catch(Exception ignored){
        node = alloc(null);
      }
      return node;
    }

    public void free(Component cf) {
      assert cf != null;
      String nodeName = FractalNavigator.getName(cf);
      log("Free " + nodeName);
      if(nodesIdx.containsKey(nodeName)){
        nodesIdx.get(nodeName).free = true;
      }else{
        log(nodeName + " don't exists");
      }
    }

    public Collection<Component> getAllocated() {
      List<Component> allocatedNodes = new LinkedList<Component>();
      for(Node n : nodes){
        if(!n.free) allocatedNodes.add(n.exec);
      }
      return allocatedNodes;
    }

    public Collection<Component> getFree() {
      List<Component> freeNodes = new LinkedList<Component>();
      for(Node n : nodes){
        if(n.free) freeNodes.add(n.exec);
      }
      return freeNodes;
    }

 // ------- ContentController -----------------------------------------------------------------
    
    public void addFcSubComponent(Component node) throws IllegalContentException, IllegalLifeCycleException {
      assert node != null;
      String name = FractalNavigator.getName(node);
      log("add node " + name);
      Node n = new Node(node);
      nodes.add(n);
      nodesIdx.put(name, n);
      sendEvent("newNode", node);
    }

    public Object getFcInternalInterface(String itfName) throws NoSuchInterfaceException {
      throw new NoSuchInterfaceException(itfName);
    }

    public Object[] getFcInternalInterfaces() {
      return new Object[] {};
    }

    public Component[] getFcSubComponents() {
      Component[] subs = new Component[nodes.size()];
      int i = 0;
      for (Node n : nodes) subs[i++]=n.exec;
      return subs;
    }

    public void removeFcSubComponent(Component node) throws IllegalContentException, IllegalLifeCycleException {
      log("remove node ");
      Node toRemove = null;
      for (Node n : nodes){
        if(n.exec.equals(node)){
          toRemove = n;
          break;
        }
      }
      if(toRemove == null){
        //log("Component is not a sub component of allocator");
        throw new IllegalContentException("Component is not a sub component of allocator");
      }else{
        nodesIdx.remove(toRemove.name);
        nodes.remove(toRemove);
      }
    }
    
    private void sendEvent(String key, Component node) {
      Event e = EventFactory.newEvent(EventTag.NEWNODE);
      e.getEventBody().put(key, node);
      try {
        eventOut.handleEvent(e);
      } catch (JadeException e1) {
        log(e1);
      }
    }
    
    protected class Node{
      public Component exec;
      public Component meta;
      public String name;
      public boolean free;
      
      public Node(Component cmp){
        exec = cmp;
        meta = Reflex.getDualComponent(cmp);
        name = FractalNavigator.getName(cmp);
        free = true;
      }
    }
    
}

